/**
 *
 */
package org.svnadmin.service;

import org.apache.commons.lang3.StringUtils;
import org.apache.log4j.Logger;
import org.springframework.stereotype.Service;
import org.svnadmin.constant.Constants;
import org.svnadmin.dao.PjDao;
import org.svnadmin.dao.PjUsrDao;
import org.svnadmin.entity.Pj;
import org.svnadmin.entity.PjUsr;
import org.svnadmin.entity.Usr;
import org.svnadmin.util.EncryptUtil;
import org.svnadmin.util.I18N;
import org.svnadmin.util.UsrProvider;
import org.tmatesoft.svn.core.*;
import org.tmatesoft.svn.core.auth.ISVNAuthenticationManager;
import org.tmatesoft.svn.core.internal.io.dav.DAVRepositoryFactory;
import org.tmatesoft.svn.core.internal.io.fs.FSRepositoryFactory;
import org.tmatesoft.svn.core.internal.io.svn.SVNRepositoryFactoryImpl;
import org.tmatesoft.svn.core.io.SVNRepository;
import org.tmatesoft.svn.core.io.SVNRepositoryFactory;
import org.tmatesoft.svn.core.wc.SVNRevision;
import org.tmatesoft.svn.core.wc.SVNWCUtil;

import javax.annotation.Resource;
import java.io.File;
import java.net.MalformedURLException;
import java.util.Collection;

/**
 * 仓库服务层
 *
 * @author <a href="mailto:yuanhuiwu@gmail.com">Huiwu Yuan</a>
 * @since 3.0.2
 */
@Service(RepositoryService.BEAN_NAME)
public class RepositoryService {

    /**
     * Bean名称
     */
    public static final String BEAN_NAME = "repositoryService";

    static {
        /*
         * For using over http:// and https://
         */
        DAVRepositoryFactory.setup();
        /*
         * For using over svn:// and svn+xxx://
         */
        SVNRepositoryFactoryImpl.setup();

        /*
         * For using over file:///
         */
        FSRepositoryFactory.setup();
    }

    /**
     * 日志
     */
    private final Logger LOG = Logger.getLogger(RepositoryService.class);
    /**
     * 项目DAO
     */
    @Resource(name = PjDao.BEAN_NAME)
    PjDao pjDao;
    /**
     * 项目用户DAO
     */
    @Resource(name = PjUsrDao.BEAN_NAME)
    PjUsrDao pjUsrDao;

    /**
     * 从项目的url中获取svn的url
     *
     * @param url 项目url
     * @return svn url
     */
    public static String parseURL(String url) {
        if (StringUtils.isBlank(url)) {
            return null;
        }
        String result = url.trim();// 去空格
        result = StringUtils.replace(result, "\t", " ");
        result = StringUtils.replace(result, "\r", " ");
        result = StringUtils.replace(result, "\n", " ");
        result = StringUtils.replace(result, "\b", " ");
        result = StringUtils.replace(result, "<", " ");// eg. <br/>
        result = StringUtils.replace(result, "(", " ");// eg. ()

        result = result.trim();
        int blank = result.indexOf(" ");
        if (blank != -1) {
            result = result.substring(0, blank);
        }

        return result;
    }

    /**
     * Creates a local blank FSFS-type repository. A call to this routine is
     * equivalent to
     * <code>createLocalRepository(path, null, enableRevisionProperties, force)</code>.
     *
     * @param respository a repository root location
     * @return a local URL (file:///) of a newly created repository
     */
    public static SVNURL createLocalRepository(File respository) {
        try {
            return SVNRepositoryFactory.createLocalRepository(respository, true, false);
        } catch (SVNException e) {
            throw new RuntimeException(I18N.getLbl("pj.save.error.createRepository", "创建仓库失败.{0}",
                    new Object[]{respository.getAbsolutePath()}) + " : " + e.getMessage());
        }
    }

    /**
     * 获取svn仓库
     *
     * @param pjId 项目ID
     * @return svn仓库
     * @throws SVNException svn异常，例如没有权限等
     */
    public SVNRepository getRepository(String pjId) throws SVNException {
        Pj pj = pjDao.get(pjId);
        if (pj == null) {
            LOG.warn("Not found project: " + pjId);
            return null;
        }
        return this.getRepository(pj);
    }

    /**
     * 获取svn仓库
     *
     * @param pj 项目
     * @return svn仓库
     * @throws SVNException          svn异常，例如没有权限等
     * @throws MalformedURLException
     * @throws Exception
     */
    public SVNRepository getRepository(Pj pj) throws SVNException {

        Usr usr = UsrProvider.getCurrentUsr();

        String svnUrl = parseURL(pj.getUrl());
        if (StringUtils.isBlank(svnUrl)) {
            throw new RuntimeException(I18N.getLbl("pj.error.url", "URL不可以为空"));
        }
        String svnUserName = usr.getUsr();
        String svnPassword = usr.getPsw();
        if (!Constants.HTTP_MUTIL.equals(pj.getType())) {
            // pj_usr覆盖用户的密码
            PjUsr pjUsr = pjUsrDao.get(pj.getPj(), svnUserName);
            if (pjUsr != null) {
                svnPassword = pjUsr.getPsw();
            }
        }
        // 兼容usvn导入的MD5密码
        if (!svnPassword.contains("apr1")) {
            svnPassword = EncryptUtil.decrypt(svnPassword);// 解密
        }
        // 兼容LDAP认证 svn1为显示目录树的情况
        if (pj.getType().startsWith(Constants.HTTP)) {
            String location = "";
            // 例如 http://192.168.1.100/svn/projar/trunk
            if (StringUtils.isNotBlank(pj.getUrl()) && pj.getUrl().indexOf("//") != -1) {
                svnUrl = RepositoryService.parseURL(pj.getUrl());
                String[] split = svnUrl.split("/");
                location="";
                if(split.length>3) {
                    location = split[3];
                }
            }
            svnUrl = svnUrl.replace("/"+location, "/"+location + "1");
        }
        SVNRepository repository = null;
        repository = SVNRepositoryFactory.create(SVNURL.parseURIDecoded(svnUrl));
        // svn的认证方式
        ISVNAuthenticationManager authManager = SVNWCUtil.createDefaultAuthenticationManager(svnUserName, svnPassword);
        repository.setAuthenticationManager(authManager);
        return repository;
    }

    /**
     * 返回项目仓库的根
     *
     * @param pj 项目
     * @return 仓库根
     */
    public String getRepositoryRoot(Pj pj) {
        SVNRepository repository = null;
        try {
            repository = this.getRepository(pj);
            return repository.getRepositoryRoot(true).toString();
        } catch (SVNAuthenticationException e) {
            LOG.error(e.getMessage());
            return null;
        } catch (SVNException e) {
            LOG.error(e.getMessage());
            e.printStackTrace();
            return null;
        } finally {
            if (repository != null) {
                repository.closeSession();
            }
        }
    }

    /**
     * 获取项目指定路径的svn仓库文件系统
     *
     * @param pj   项目
     * @param path 相对仓库根目录的路径
     * @return 目录或文件系统
     */
    @SuppressWarnings({"unchecked", "rawtypes"})
    public Collection<SVNDirEntry> getDir(String pj, String path) {
        int i = 0;
        if (StringUtils.isBlank(path)) {
            path = "/";// root
        }
        if (!path.startsWith("/")) {
            path = "/" + path;
        }
        SVNRepository repository = null;
        try {
            repository = this.getRepository(pj);
            SVNProperties properties = new SVNProperties();
            return repository.getDir(path, SVNRevision.HEAD.getNumber(), properties, (Collection) null);
        } catch (SVNAuthenticationException e) {
            e.printStackTrace();
            throw new RuntimeException(I18N.getLbl("svn.auth.error", "认证失败"));
        } catch (SVNException e) {
            e.printStackTrace();
            throw new RuntimeException(e.getMessage());
        } finally {
            if (repository != null) {
                repository.closeSession();
            }
        }
    }

}
